package servlet;

import utilities.Utilities;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.openid4java.OpenIDException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ax.FetchRequest;
import static utilities.Utilities.*;

/**
 * Servlet per la comunicazione con OpenId.
 * @author Ale
 */
public class OpenIdServlet extends HttpServlet {

    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");

        if (action == null) {
            Utilities.errore("Nessuna action specificata alla OpenIdServlet", request, response);
        } else if (action.equals("authDoneRegistrazione")) {
            authDoneRegistrazione(request, response);
        } else if (action.equals("authDoneLogin")) {
            authDoneLogin(request, response);
        } else if (action.equals("registraUtenteSocial")) {
            registraUtenteSocial(request, response);
        } else if (action.equals("reallyLoginOpenId")) {
            loginOpenId(request, response);
        }
    }

    private void registraUtenteSocial(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String social = request.getParameter("social");
        authRequest(getUrlIdentifier(social), "authDoneRegistrazione", request, response);
    }

    private void loginOpenId(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        String social = request.getParameter("social");
        authRequest(getUrlIdentifier(social), "authDoneLogin", request, response);
    }

    private void authDoneLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Map<String, String[]> pm = request.getParameterMap();

        if ("cancel".equals(pm.get("openid.mode")[0])) {
            errore("Devi consentire l'accesso ai tuoi dati per loggarti.", request, response);
        }

        String email = "";
        String nome = "";
        String cognome = "";
        try {
            // google
            email = pm.get("openid.ext1.value.gmail")[0];
            nome = pm.get("openid.ext1.value.firstName")[0];
            cognome = (String) pm.get("openid.ext1.value.lastName")[0];
        } catch (NullPointerException npe) {
            // yahoo
            email = pm.get("openid.ax.value.email")[0];
            String fullname = pm.get("openid.ax.value.fullname")[0];
            String[] tokens = fullname.split(" ");
            nome = tokens[0];
            for (int i = 1; i < tokens.length; i++) {
                cognome += tokens[i] + " ";
            }
        }

        request.setAttribute("mail", email);
        request.setAttribute("nome", nome);
        request.setAttribute("cognome", cognome);
        forward("/Controller?action=loginOpenId", request, response);
    }

    private void authDoneRegistrazione(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Map<String, String[]> pm = request.getParameterMap();

        if ("cancel".equals(pm.get("openid.mode")[0])) {
            Utilities.errore("Devi consentire l'accesso ai tuoi dati per registrati.", request, response);
        }

        String email = "";
        String nome = "";
        String cognome = "";
        try {
            // google
            email = pm.get("openid.ext1.value.gmail")[0];
            nome = pm.get("openid.ext1.value.firstName")[0];
            cognome = (String) pm.get("openid.ext1.value.lastName")[0];
        } catch (NullPointerException npe) {
            // yahoo
            email = pm.get("openid.ax.value.email")[0];
            String fullname = pm.get("openid.ax.value.fullname")[0];
            String[] tokens = fullname.split(" ");
            nome = tokens[0];
            for (int i = 1; i < tokens.length; i++) {
                cognome += tokens[i] + " ";
            }
        }

        request.setAttribute("email", email);
        request.setAttribute("nome", nome);
        request.setAttribute("cognome", cognome);
        forward("/registrazioneUtenteSocial.jsp", request, response);
    }

    // --- placing the authentication request ---
    private String authRequest(String userSuppliedString, String nextAction, HttpServletRequest httpReq, HttpServletResponse httpResp) throws IOException, ServletException {
        ConsumerManager manager = new ConsumerManager();

        try {
            // configure the return_to URL where your application will receive
            // the authentication responses from the OpenID provider
            String returnToUrl = "http://localhost:8080/ShippingShareBisBis-war/Controller?action=" + nextAction;

            // --- Forward proxy setup (only if needed) ---
            // ProxyProperties proxyProps = new ProxyProperties();
            // proxyProps.setProxyName("proxy.example.com");
            // proxyProps.setProxyPort(8080);
            // HttpClientFactory.setProxyProperties(proxyProps);

            // perform discovery on the user-supplied identifier
            List discoveries = manager.discover(userSuppliedString);

            // attempt to associate with the OpenID provider
            // and retrieve one service endpoint for authentication
            DiscoveryInformation discovered = manager.associate(discoveries);

            // store the discovery information in the user's session
            httpReq.getSession().setAttribute("openid-disc", discovered);

            // obtain a AuthRequest message to be sent to the OpenID provider
            AuthRequest authReq = manager.authenticate(discovered, returnToUrl);

            // Attribute Exchange example: fetching the 'email' attribute
            FetchRequest fetch = FetchRequest.createFetchRequest();
            fetch.addAttribute("gmail", "http://schema.openid.net/contact/email", true);
            fetch.addAttribute("ymail", "http://axschema.org/contact/email", true);
            fetch.addAttribute("firstName", "http://axschema.org/namePerson/first", true);
            fetch.addAttribute("lastName", "http://axschema.org/namePerson/last", true);
            fetch.addAttribute("fullname", "http://axschema.org/namePerson", true);

            // attach the extension to the authentication request
            authReq.addExtension(fetch);


            if (!discovered.isVersion2()) {
                // Option 1: GET HTTP-redirect to the OpenID Provider endpoint
                // The only method supported in OpenID 1.x
                // redirect-URL usually limited ~2048 bytes
                httpResp.sendRedirect(authReq.getDestinationUrl(true));
                return null;
            } else {
                // Option 2: HTML FORM Redirection (Allows payloads >2048 bytes)

                RequestDispatcher dispatcher =
                        getServletContext().getRequestDispatcher("/WEB-INF/openid/formredirection.jsp");
                httpReq.setAttribute("parameterMap", authReq.getParameterMap());
                httpReq.setAttribute("destinationUrl", authReq.getDestinationUrl(false));
                dispatcher.forward(httpReq, httpResp);
            }
        } catch (DiscoveryException de) {
            errore(de.getMessage(), httpReq, httpResp);
        } catch (OpenIDException e) {
            // present error to the user
            errore(e.getMessage(), httpReq, httpResp);
        }

        return null;
    }

    private String getUrlIdentifier(String social) {
        if ("google".equals(social)) {
            return "https://www.google.com/accounts/o8/id";
        } else if ("yahoo".equals(social)) {
            return "https://me.yahoo.com";
        }
        return "";
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>
    
}
